package cn.dansj.common.utils.spring.reflect;

import cn.dansj.common.utils.transfer.Transformation;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.util.Assert;

import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public abstract class ProxyFactory {
    // 缓存接口注解配置（Key: 接口类名）
    private static final Map<String, AroundAdvice> interfaceAdviceCache = new ConcurrentHashMap<>();

    /**
     * 继承接口的类返回值使用接口类，不能使用继承类
     */
    public static <T> T createProxy(Class<T> tClass, Object... args) {
        return Transformation.lambdaTryCatch(() -> createProxy(tClass.getDeclaredConstructor().newInstance(), args), () -> {
            throw new IllegalArgumentException(tClass + " 缺少无参构造函数");
        });
    }

    /**
     * 直接传递Advice 不需要AroundAdvice注解
     * 不传递Advice 需要在接口或者类上面添加AroundAdvice注解信息
     * <p>
     * 继承接口的类返回值使用接口类，不能使用继承类
     */
    public static <T> T createProxy(T target, Object... args) {
        if (target == null) throw new IllegalArgumentException("Target object cannot be null");
        Advice advice = null;
        List<String> methodNames = new ArrayList<>();
        for (Object arg : args) {
            if (arg instanceof Class<?> && Advice.class.isAssignableFrom((Class<?>) arg)) {
                advice = Transformation.lambdaTryCatch(() -> (Advice) ((Class<?>) arg).getDeclaredConstructor().newInstance(), () -> null);
            }
            if (arg instanceof Advice) advice = (Advice) arg;
            if (arg instanceof String) methodNames.add((String) arg);
        }

        Class<?> targetClass = target.getClass();
        if (hasInterface(targetClass)) {
            return createJdkProxy(target, advice, methodNames);
        } else {
            return createCglibProxy(target, advice, methodNames);
        }
    }

    private static boolean hasInterface(Class<?> clazz) {
        return InterfaceUtils.getAllInterfaces(clazz).length > 0;
    }

    /**
     * JDK动态代理实现 有参无参构造都可以
     */
    @SuppressWarnings("unchecked")
    private static <T> T createJdkProxy(T target, Advice advice, List<String> methodNames) {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), InterfaceUtils.getAllInterfaces(target.getClass()), new EnhancedInvocationHandler(target, advice, methodNames));
    }

    /**
     * CGLIB代理实现 必须要有无参构造 ==> enhancer.create()
     */
    @SuppressWarnings("unchecked")
    private static <T> T createCglibProxy(T target, Advice advice, List<String> methodNames) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibMethodInterceptor(target, advice, methodNames));
        return (T) enhancer.create();
    }

    public static Class<?> getRealClass(Object proxyInstance) {
        try {
            return getJdkProxyTargetObject(proxyInstance).getClass();
        } catch (Exception e) {
            return gettCglibProxyTargetClass(proxyInstance);
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T getRealTarget(T proxyInstance) {
        try {
            return (T) getJdkProxyTargetObject(proxyInstance);
        } catch (Throwable jdkException) {
            try {
                return (T) getCglibProxyTargetObject(proxyInstance);
            } catch (Throwable cglibException) {
                return proxyInstance;
            }
        }
    }

    private static Object getJdkProxyTargetObject(Object proxyInstance) throws IllegalAccessException, NoSuchFieldException {
        InvocationHandler handler = Proxy.getInvocationHandler(proxyInstance);
        Field target = handler.getClass().getDeclaredField("target");
        target.setAccessible(true);
        return target.get(handler);
    }

    private static Object getCglibProxyTargetObject(Object proxy) throws IllegalAccessException, NoSuchFieldException {
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);
        Field target = dynamicAdvisedInterceptor.getClass().getDeclaredField("target");
        target.setAccessible(true);
        return target.get(dynamicAdvisedInterceptor);
    }

    private static Class<?> gettCglibProxyTargetClass(Object candidate) {
        Assert.notNull(candidate, "Candidate object must not be null");
        Class<?> result = null;
        if (candidate instanceof TargetClassAware) {
            result = ((TargetClassAware) candidate).getTargetClass();
        }

        if (result == null) {
            result = isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass();
        }

        return result;
    }

    public static boolean isCglibProxy(Object object) {
        return (object instanceof SpringProxy || object.getClass().getName().contains("EnhancerByCGLIB")) && object.getClass().getName().contains("$$");
    }

    public static boolean isJdkProxy(Object object) {
        return Proxy.isProxyClass(object.getClass());
    }

    private static boolean matchesMethod(List<String> methodNames, Method method) {
        return methodNames.isEmpty() || methodNames.contains(method.getName());
    }

    // JDK代理处理器（含缓存优化）
    private static class EnhancedInvocationHandler implements InvocationHandler {
        private final Object target;
        private final Map<Method, AroundAdvice> methodAdviceCache = new ConcurrentHashMap<>();
        private final Advice advice;
        private final List<String> methodNames;

        public EnhancedInvocationHandler(Object target, Advice advice, List<String> methodNames) {
            this.target = target;
            this.advice = advice;
            this.methodNames = methodNames;
            cacheInterfaceAdvice();
        }

        private void cacheInterfaceAdvice() {
            for (Class<?> interfaceClass : InterfaceUtils.getAllInterfaces(target.getClass())) {
                AroundAdvice advice = findAnnotationWithInheritance(interfaceClass);
                if (advice != null) {
                    interfaceAdviceCache.put(interfaceClass.getName(), advice);
                }
            }
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            final AroundAdvice aroundAdvice = methodAdviceCache.computeIfAbsent(method, m -> findMatchingAdvice(method));
            final Advice innerAdvice = (advice == null) ? aroundAdvice == null ? null : aroundAdvice.interceptor().getDeclaredConstructor().newInstance() : ProxyFactory.matchesMethod(methodNames, method) ? advice : null;
            try {
                executeAdviceBefore(innerAdvice, method, args);
                Object result = method.invoke(target, args);
                return executeAdviceAfter(innerAdvice, method, args, result);
            } catch (InvocationTargetException invocationTargetException) {
                throw Transformation.nvl(executeAdviceException(innerAdvice, method, args, invocationTargetException.getCause()), invocationTargetException.getCause());
            }
        }

        private AroundAdvice findMatchingAdvice(Method method) {
            Class<?> declaringClass = method.getDeclaringClass();
            AroundAdvice advice = interfaceAdviceCache.get(declaringClass.getName());
            if (advice != null && matchesMethod(advice, method)) {
                return advice;
            }
            return null;
        }

        private boolean matchesMethod(AroundAdvice advice, Method method) {
            return advice.methods().length == 0 || Arrays.asList(advice.methods()).contains(method.getName());
        }
    }

    // CGLIB方法拦截器
    private static class CglibMethodInterceptor implements MethodInterceptor {
        private final Object target;
        private final Map<Method, AroundAdvice> adviceCache = new ConcurrentHashMap<>();
        private final Advice advice;
        private final List<String> methodNames;

        public CglibMethodInterceptor(Object target, Advice advice, List<String> methodNames) {
            this.target = target;
            this.advice = advice;
            this.methodNames = methodNames;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            final Advice innerAdvice = advice == null ? adviceCache.computeIfAbsent(method, m -> findAnnotationWithInheritance(target.getClass())).interceptor().getDeclaredConstructor().newInstance() : ProxyFactory.matchesMethod(methodNames, method) ? advice : null;
            try {
                executeAdviceBefore(innerAdvice, method, args);
                Object result = proxy.invoke(target, args);
                return executeAdviceAfter(innerAdvice, method, args, result);
            } catch (Throwable e) {
                throw Transformation.nvl(executeAdviceException(innerAdvice, method, args, e), e);
            }
        }
    }

    // 带继承关系的注解查找
    private static AroundAdvice findAnnotationWithInheritance(Class<?> clazz) {
        // 递归查找类注解
        for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
            AroundAdvice advice = c.getAnnotation(AroundAdvice.class);
            if (advice != null) return advice;
        }

        // 递归查找接口注解
        return findInterfaceAnnotation(clazz);
    }

    private static AroundAdvice findInterfaceAnnotation(Class<?> clazz) {
        if (clazz == null) return null;
        Queue<Class<?>> queue = new LinkedList<>();
        Collections.addAll(queue, InterfaceUtils.getAllInterfaces(clazz));

        while (!queue.isEmpty()) {
            Class<?> interfaceClass = queue.poll();
            AroundAdvice advice = interfaceClass.getAnnotation(AroundAdvice.class);
            if (advice != null) return advice;

            Collections.addAll(queue, InterfaceUtils.getAllInterfaces(interfaceClass));
        }
        return null;
    }

    // 带缓存的通知方法执行
    private static Object executeAdviceAfter(Advice advice, Method targetMethod, Object[] args, Object result) {
        if (advice == null) return result;
        try {
            return advice.after(targetMethod, args, result);
        } catch (Exception e) {
            throw new RuntimeException("执行通知方法失败: ", e);
        }
    }

    private static void executeAdviceBefore(Advice advice, Method targetMethod, Object[] args) {
        if (advice == null) return;

        try {
            advice.before(targetMethod, args);
        } catch (Exception e) {
            throw new RuntimeException("执行通知方法失败: ", e);
        }
    }

    private static Throwable executeAdviceException(Advice advice, Method targetMethod, Object[] args, Throwable ex) {
        if (advice == null) return ex;

        try {
            return advice.exception(targetMethod, args, ex);
        } catch (Exception e) {
            throw new RuntimeException("执行通知方法失败: ", e);
        }
    }

    private static class InterfaceUtils {
        // 使用 ConcurrentHashMap 作为缓存，支持并发访问
        private static final Map<Class<?>, Class<?>[]> interfaceCache = new ConcurrentHashMap<>();

        /**
         * 获取类的所有接口（包括父类和接口继承的接口）
         *
         * @param clazz 目标类
         * @return 所有接口的集合
         */
        public static Class<?>[] getAllInterfaces(Class<?> clazz) {
            // 检查缓存中是否已存在结果
            if (interfaceCache.containsKey(clazz)) {
                return interfaceCache.get(clazz);
            }

            // 使用 LinkedHashSet 保持插入顺序并去重
            Set<Class<?>> interfaces = new LinkedHashSet<>();
            getAllInterfacesRecursive(clazz, interfaces);

            // 将结果存入缓存
            removeCache(clazz.getName());
            final Class<?>[] value = interfaces.toArray(new Class[]{});
            interfaceCache.put(clazz, value);
            return value;
        }

        private static void removeCache(String name) {
            interfaceCache.forEach((key, value) -> {
                if (key.getName().equals(name)) {
                    interfaceCache.remove(key);
                }
            });
        }

        /**
         * 递归获取类的所有接口
         *
         * @param clazz      目标类
         * @param interfaces 存储接口的集合
         */
        private static void getAllInterfacesRecursive(Class<?> clazz, Set<Class<?>> interfaces) {
            // 处理当前类的直接接口
            for (Class<?> interfaceClass : clazz.getInterfaces()) {
                if (interfaces.add(interfaceClass)) { // 如果成功添加到集合中，说明是新接口
                    getAllInterfacesRecursive(interfaceClass, interfaces); // 递归获取父接口
                }
            }

            // 处理父类
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                getAllInterfacesRecursive(superClass, interfaces); // 递归获取父类的接口
            }
        }
    }
}